package de.tempel.pong.collison;

import java.awt.geom.Point2D;
import java.util.List;

import processing.core.PApplet;
import de.tempel.pong.objects.Ball;
import de.tempel.pong.objects.Racket;
import de.tempel.pong.util.Line2D;
import de.tempel.pong.util.Rectangle2D;

/**
 * 
 * Manager für ContinousCollisionDetection.
 * 
 * @author mathias
 * 
 */
public class ContinuousCollisionManager extends AbstractCollisionManager {

	/**
	 * 
	 * @param ball
	 * @param rackets
	 * @param window
	 */
	public ContinuousCollisionManager(Ball ball, List<Racket> rackets,
			PApplet window) {
		super(ball, rackets, window);
	}

	@Override
	public void checkCollisions(float tpf) {

		boolean hasCollisionWithWall = checkCollisionWithWall(ball, tpf);
		if (hasCollisionWithWall) {
			return;
		}

		Point2D ballMiddle = ball.getMiddlePosition();
		Line2D ballMovementLine = ball.getMovementLine();

		for (Racket r : rackets) {

			if (r.getRectangle().intersectsLine(ballMovementLine)) {

				Point2D realIntersectionPoint = r.getRectangle()
						.intersectionPoint(ballMovementLine, ballMiddle);

				if (realIntersectionPoint == null) {
					System.err.println("Error calculating collision!");
					return;
				}

				// Berechne Zeit bis zur Kollision
				double timeToCollision = collisionTime(realIntersectionPoint,
						ball);

				// Wenn Kollision unmittelbar bevorsteht, wird Ball auf die
				// Position des Kollisionspunktes gesetzt und seine
				// Bewegungsrichtung geändert
				if (timeToCollision < tpf) {

					Point2D ballPosition = ball.getMiddlePosition();

					// Ball links vom Schläger
					if (ballPosition.getX() < r.getRectangle().getX()) {
						Point2D newBallPosition = new Point2D.Double(
								realIntersectionPoint.getX() - ball.getWidth(),
								realIntersectionPoint.getY());
						ball.setPosition(newBallPosition);
						ball.changeXMovementDirection();
						// Ball rechts vom Schläger
					} else {
						Point2D newBallPosition = new Point2D.Double(
								realIntersectionPoint.getX(),
								realIntersectionPoint.getY());
						ball.setPosition(newBallPosition);
						ball.changeXMovementDirection();
					}

				}

			}
		}

	}

	private boolean checkCollisionWithWall(Ball b, float tpf) {

		Line2D ballMovementLine = ball.getMovementLine();

		double x0Upper = ballMovementLine.xForY(0d);
		double x0Lower = ballMovementLine.xForY(window.getHeight());

		Point2D upperCollisionPoint = new Point2D.Double(x0Upper, 0d);
		Point2D lowerCollisionPoint = new Point2D.Double(x0Lower,
				window.getHeight());

		Rectangle2D windowRectangle = new Rectangle2D(-1d, -1d,
				window.getWidth() + 2, window.getHeight() + 2);

		Point2D realCollisionPoint;
		if (ball.getMovement().getY() > 0) {
			realCollisionPoint = lowerCollisionPoint;
		} else {
			realCollisionPoint = upperCollisionPoint;
		}

		if (windowRectangle.contains(realCollisionPoint)) {

			double collisionTime = collisionTime(realCollisionPoint, ball);

			if (collisionTime < tpf) {

				ball.changeYMovementDirection();

				return true;
			}

		}

		return false;

	}

	/**
	 * 
	 * Gibt Kollisionszeitpunkt von Ball und Punkt zurück
	 * 
	 * @param collisionPoint
	 * @param ball
	 * @return
	 */
	private double collisionTime(Point2D collisionPoint, Ball ball) {

		Point2D ballMiddle = ball.getMiddlePosition();

		double distanceToCollisionPoint = ballMiddle.distance(collisionPoint);
		double speed = ball.getMovement().length();
		double timeToCollision = distanceToCollisionPoint / speed;

		return timeToCollision;
	}

}
